home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1999 June
/
Macworld (1999-06).dmg
/
Shareware World
/
Info
/
For Developers
/
MacZoop2.0.sea
/
MacZoop2.0
/
Required Classes
/
ZoopUtilities.cpp
< prev
next >
Wrap
Text File
|
1999-02-23
|
18KB
|
654 lines
/*************************************************************************************************
*
*
* MacZoop 2.0 - "the framework for the rest of us"
*
*
*
* ZoopUtilities.cpp -- general utilities
*
*
*
*
*
* © 1999, Graham Cox
*
*
*
*
*************************************************************************************************/
#include "MacZoop.h"
#include <FixMath.h>
#include <fp.h>
#include <palettes.h>
Rect gZoomFXSourceRect = { 0, 0, 0, 0 };
/*--------------------------------*** COPYPSTRING ***---------------------------------*/
/*
copy <srcString> to <destString>
----------------------------------------------------------------------------------------*/
void CopyPString( ConstStr255Param srcString, Str255 destString )
{
BlockMoveData( srcString, destString, MIN( srcString[0] + 1, 255 ));
}
/*------------------------------*** CONCATPSTRINGS ***--------------------------------*/
/*
concatenate <append> to <root>
----------------------------------------------------------------------------------------*/
void ConcatPStrings( Str255 root, ConstStr255Param append )
{
short charsToCopy;
// Truncate if concatenated string would be longer than 255 chars.
charsToCopy = MIN( append[0], 255 - root[0]);
BlockMoveData( append + 1, root + root[0] + 1, (long) charsToCopy);
root[0] += charsToCopy;
}
/*-----------------------------*** COPYPSTRINGTRUNC ***-------------------------------*/
/*
Copy <srcString> to <destString>, limiting maximum length of destString to <ccLim>
----------------------------------------------------------------------------------------*/
void CopyPStringTrunc( ConstStr255Param srcString, Str255 destString, unsigned char ccLim )
{
BlockMoveData( srcString, destString, MIN( srcString[0] + 1, ccLim + 1 ));
destString[0] = MIN( srcString[0], ccLim );
}
/*---------------------------*** CONCATPSTRINGSTRUNC ***------------------------------*/
/*
Concatenate <append> to <root>, but limiting total string length to a maximum of <ccLim>
----------------------------------------------------------------------------------------*/
void ConcatPStringsTrunc( Str255 root, ConstStr255Param append, unsigned char ccLim )
{
short charsToCopy;
// Truncate if concatenated string would be longer than <ccLim> chars.
charsToCopy = MIN( append[0], ccLim - root[0]);
BlockMoveData( append + 1, root + root[0] + 1, (long) charsToCopy);
root[0] += charsToCopy;
}
/*------------------------------*** COPYCTOPSTRING ***--------------------------------*/
/*
converts a 'C' null-terminated string up to 255 chars long to a Mac-style pascal string.
Note that unlike c2pstr, etc, this does not modify the original string.
----------------------------------------------------------------------------------------*/
void CopyCToPString( char* cStringIn, Str255 pStringOut )
{
unsigned char u = 0;
while( cStringIn[u++] ){}; // no error here, ignore warning.
if ( u > 255 )
u = 255;
BlockMoveData( cStringIn, &pStringOut[1], u );
pStringOut[0] = u;
}
/*--------------------------------*** ISCOLOURPORT ***--------------------------------*/
/*
returns TRUE if the grafport passed is colour, otherwise FALSE
----------------------------------------------------------------------------------------*/
Boolean IsColourPort( GrafPtr aPort )
{
return (( aPort->portBits.rowBytes & 0x8000 ) != 0 );
}
/*------------------------------*** SETPORTBLACKWHITE ***-----------------------------*/
/*
sets the current port to black & white
----------------------------------------------------------------------------------------*/
void SetPortBlackWhite()
{
RGBForeColor( &gBlack );
RGBBackColor( &gWhite );
}
/*-------------------------------*** SETHILITEMODE ***--------------------------------*/
/*
set up hiliting mode for inversion operation
----------------------------------------------------------------------------------------*/
void SetHiliteMode()
{
LMSetHiliteMode( LMGetHiliteMode() & pHiliteBit );
}
/*----------------------------------*** MACHASDM ***----------------------------------*/
/*
static function returns TRUE if Drag Manager is present. Checks link on PowerMacs.
----------------------------------------------------------------------------------------*/
Boolean MacHasDM()
{
// returns TRUE if drag manager is installed on this mac and is loaded by the CFM.
Boolean hasDM = gMacInfo.hasDragManager;
#if GENERATINGCFM
// check that the dragLib is actually loaded and linked
hasDM = hasDM && ( NewDrag != (void*) kUnresolvedCFragSymbolAddress );
#endif
return hasDM;
}
/*--------------------------------*** NOTIFYALERT ***---------------------------------*/
/*
Works just like Alert(), except that if the app is in the background, the notification
manager is used to inform the user that the app requires attention.
----------------------------------------------------------------------------------------*/
static NMRec gNotification;
static Str255 gNotifyMessage;
static Boolean gNotificationPosted = FALSE;
short NotifyAlert( const short alertID, NTAlertFlags ntFlags )
{
// basically: if ( background ):
// install notification
// wait for app foreground
// delete notification
if ( ! gNotificationPosted )
{
if ( gApplication->InBackground())
{
// if an alert is to be displayed, set it up
if ( ntFlags & ntAlertDisplayMessage )
{
// build a string that says "The application “<name>” requires your attention.
// Please bring it to the front.
Str255 appName;
GetIndString( gNotifyMessage, kMiscStrListID, 17 );
gApplication->GetName( appName );
ConcatPStrings( gNotifyMessage, appName );
GetIndString( appName, kMiscStrListID, 18 );
ConcatPStrings( gNotifyMessage, appName );
gNotification.nmStr = gNotifyMessage;
}
else
gNotification.nmStr = NULL;
// set up any sound:
if ( ntFlags & ntAlertPlaySound )
gNotification.nmSound = ( Handle ) -1L;
else
gNotification.nmSound = NULL;
Handle appIconSuiteH;
OSErr theErr;
// set up icon and menu mark
gNotification.qType = 8;
gNotification.nmMark = TRUE;
gNotification.nmResp = NULL;
gNotification.nmRefCon = NULL;
theErr = GetIconSuite( &appIconSuiteH, kApplicationIconSuiteID, svAllAvailableData );
if ( theErr == noErr )
gNotification.nmIcon = appIconSuiteH;
else
gNotification.nmIcon = NULL;
// install notification:
NMInstall( &gNotification );
gNotificationPosted = TRUE;
// handle events until we come back to the front:
gApplication->WaitApplicationForeground();
// we're back, so delete the notification
NMRemove( &gNotification );
gNotificationPosted = FALSE;
if ( appIconSuiteH )
DisposeIconSuite( appIconSuiteH, FALSE );
}
if ( alertID > 0 )
{
StopCursorAnimation();
SetCursorShape( ARROW_CURSOR );
return Alert( alertID, NULL );
}
else
return ok;
}
else
{
// what can we do? This shouldn't arise in normal use, but just in case, we act as
// though the user saw an alert and clicked OK. Not ideal, but since a notification
// can't interrupt another, there's little choice.
SysBeep( 1 );
return ok;
}
}
/*---------------------------------*** MZDELAY ***------------------------------------*/
/*
delay for <ticks>. Header invariant version of toolbox Delay()
----------------------------------------------------------------------------------------*/
void MZDelay( short ticks )
{
#ifdef __COMPATIBILITY__
long ignored;
#else
unsigned long ignored;
#endif
Delay( ticks, &ignored );
}
/*----------------------------------*** MZWAIT ***------------------------------------*/
/*
Wait for <ticks>. This continues to process events duringthe wait period
----------------------------------------------------------------------------------------*/
void MZWait( unsigned short ticks )
{
long tc = TickCount() + ticks;
while( TickCount() < tc )
gApplication->Process1Event();
}
/*---------------------------------*** ASSERTERR ***----------------------------------*/
/*
report an assertion error to the user via an alert
----------------------------------------------------------------------------------------*/
void AssertErr( long lineNo, char* srcFile, char* reason, long val )
{
#if _DEBUG_
Str15 lineStr, valStr;
Str255 fileStr, reasonStr;
NumToString( lineNo, lineStr );
NumToString( val, valStr );
CopyCToPString( srcFile, fileStr );
CopyCToPString( reason, reasonStr );
ParamText( lineStr, fileStr, reasonStr, valStr );
StopCursorAnimation();
(void) Alert( kAssertionAlertID, NULL );
FailOSErr( kSilentErr );
#endif
}
/*----------------------------*** SETGLOBALZOOMSOURCE ***-----------------------------*/
/*
set the zoom source to the passed global rect
----------------------------------------------------------------------------------------*/
void SetGlobalZoomSource( Rect* aGlobalRect )
{
gZoomFXSourceRect = *aGlobalRect;
}
/*-----------------------------*** SETLOCALZOOMSOURCE ***-----------------------------*/
/*
set the zoom source to the passed local rectangle
----------------------------------------------------------------------------------------*/
void SetLocalZoomSource( Rect* aLocalRect )
{
Rect r = *aLocalRect;
LocalToGlobal( &topLeft( r ));
LocalToGlobal( &botRight( r ));
SetGlobalZoomSource( &r );
}
/*----------------------------------*** EQUALMEM ***----------------------------------*/
/*
compare two blocks of memory and return TRUE if they are equal. Zero-length data is not
considered equal.
----------------------------------------------------------------------------------------*/
Boolean EqualMem( void* a, void* b, const unsigned long length )
{
Boolean result = (length > 0);
Ptr aa, bb;
register unsigned long len = length;
aa = (Ptr) a;
bb = (Ptr) b;
while( len-- )
{
if ( *aa++ != *bb++ )
{
result = FALSE;
break;
}
}
return result;
}
/*--------------------------------*** EQUALHANDLE ***---------------------------------*/
/*
compare the contents of two handles and return TRUE if they're equal. If one or both
handles are NULL, this returns FALSE. Handles with differing sizes are not considered
equal even if the smaller handle's data matches the other handle's data exactly for its
length. Internally this calls EqualMem above. If both handles are empty or zero sized,
this will return FALSE, even though they are "equal" in one sense.
----------------------------------------------------------------------------------------*/
Boolean EqualHandle( Handle a, Handle b )
{
long siza, sizb;
if ( a == NULL || b == NULL )
return FALSE;
siza = GetHandleSize( a );
sizb = GetHandleSize( b );
if ( siza != sizb )
return FALSE;
// n.b-- no need to lock handles- EqualMem cannot move memory.
return EqualMem( *a, *b, siza );
}
/*--------------------------------*** SCALE2RECTS ***---------------------------------*/
/*
this function resizes <theRect> so that it will fit into <refRect>, but preserving its
original aspect ratio. This is useful for preventing unwanted stretching of pictures
both for creation and display of preview images. Note this also centres the rect within
the refRect for the shorter dimension. This is the more intuitively correct behaviour
----------------------------------------------------------------------------------------*/
void Scale2Rects( Rect *theRect, Rect *refRect )
{
Fixed aspectRatio;
Rect destFrame;
short refWidth,refHeight;
destFrame.top = destFrame.left = 0;
OffsetRect(theRect,-theRect->left,-theRect->top);
refWidth = refRect->right - refRect->left;
refHeight = refRect->bottom - refRect->top;
if (theRect->right < theRect->bottom)
{
// the rect is taller than it is wide, so we centre horizontally
aspectRatio = FixRatio(theRect->right,theRect->bottom);
destFrame.right = FixRound(FixMul(aspectRatio,FixRatio(refHeight,1)));
destFrame.bottom = refHeight;
OffsetRect(&destFrame,refRect->left + ((refWidth - destFrame.right) / 2),refRect->top);
}
else
{
// the rect is wider than it is tall, so we centre vertically
aspectRatio = FixRatio(theRect->bottom,theRect->right);
destFrame.bottom = FixRound(FixMul(aspectRatio,FixRatio(refWidth,1)));
destFrame.right = refWidth;
OffsetRect(&destFrame,refRect->left,refRect->top + ((refHeight - destFrame.bottom) / 2));
}
*theRect = destFrame;
}
/*-------------------------------*** REALTOSTRING ***---------------------------------*/
/*
converta real value <num>, into a pascal string, to <decPlaces> accuracy
----------------------------------------------------------------------------------------*/
void RealToString( const double num, Str255& str, short decPlaces )
{
decimal d;
decform df;
df.style = FIXEDDECIMAL;
df.digits = decPlaces;
num2dec( &df, num, &d );
dec2str( &df, &d, (char*) &str[1] );
// set length of pascal string by scanning until we reach null terminator
str[0] = 0;
while( str[++str[0]] ){}; // no error here, ignore warning.
}
/*-------------------------------*** FRAMEGRAYRECT ***---------------------------------*/
/*
draw a 3D effect frame around <aRect>
----------------------------------------------------------------------------------------*/
void FrameGrayRect( Rect* aRect )
{
Rect globRect;
GDHandle aDev;
RGBColor aColour;
RGBColor bColour;
// frames a rectangle using two shades of gray so that the rectangle appears to
// be recessed into a gray surface. This actually draws 1 pixel outside the
// passed rectangle, so that normal FrameRect calls work as expected in addition.
PenNormal();
globRect = *aRect;
LocalToGlobal( &topLeft( globRect ));
LocalToGlobal( &botRight( globRect ));
aDev = GetMaxDevice( &globRect );
GetBackColor( &aColour );
bColour.red = bColour.green = bColour.blue = 0xFFFF;
//GetGray( aDev, &aColour, &bColour );
RGBForeColor( &bColour );
MoveTo( aRect->left, aRect->bottom );
LineTo( aRect->right, aRect->bottom );
LineTo( aRect->right, aRect->top );
bColour.red = bColour.green = bColour.blue = 0;
GetGray( aDev, &aColour, &bColour );
RGBForeColor( &bColour );
Move( 0, -1 );
LineTo( aRect->left - 1, aRect->top - 1 );
LineTo( aRect->left - 1, aRect->bottom );
ForeColor( blackColor );
}
/*-------------------------------*** ETCHGRAYRECT ***---------------------------------*/
/*
draw a 3D "etched" frame around <aRect>
----------------------------------------------------------------------------------------*/
void EtchGrayRect( Rect* aRect )
{
Rect r, globRect;
GDHandle aDev;
RGBColor aColour;
RGBColor bColour;
// frames a rectangle using two shades of gray so that the rectangle appears to
// be recessed into a gray surface. This differs from FrameGrayRect in that it draws
//a 2-pixel rect that appears to recess into the surface, for decorative purposes.
PenNormal();
globRect = r = *aRect;
LocalToGlobal( &topLeft( globRect ));
LocalToGlobal( &botRight( globRect ));
aDev = GetMaxDevice( &globRect );
GetBackColor( &aColour );
bColour.red = bColour.green = bColour.blue = 0xFFFF;
//GetGray( aDev, &aColour, &bColour );
RGBForeColor( &bColour );
FrameRect( &r );
bColour.red = bColour.green = bColour.blue = 0;
GetGray( aDev, &aColour, &bColour );
RGBForeColor( &bColour );
OffsetRect( &r, -1, -1 );
FrameRect( &r );
ForeColor( blackColor );
}
/*----------------------------*** GETMAINSCREENDEPTH ***------------------------------*/
/*
return pixel depth of main (startup) screen
----------------------------------------------------------------------------------------*/
short GetMainScreenDepth()
{
GDHandle ms;
ms = GetMainDevice();
return (*(*ms)->gdPMap)->pixelSize;
}
/*-------------------------------*** SHIFTPATTERN ***---------------------------------*/
/*
cycle a black and white pattern
----------------------------------------------------------------------------------------*/
void ShiftPattern( Pattern* aPat )
{
unsigned char topRow, i;
topRow = aPat->pat[0];
for( i = 0; i < 7; i++ )
aPat->pat[i] = aPat->pat[i + 1];
aPat->pat[7] = topRow;
}
/*--------------------------------*** ANTSREGION ***----------------------------------*/
/*
implement marching ants by framing the region in a fixed striped pattern. The pattern is
cycled once each time this is called, and a timer is used to keep the rate constant- you
can thus call this repeatedly to animate marching ants around a region. Be careful not
to make the region too complex.
----------------------------------------------------------------------------------------*/
#define kAntsRate 2
void AntsRegion( RgnHandle aRgn )
{
static Pattern antsPat = { 0x0F, 0x1E, 0x3C, 0x78, 0xF0, 0xE1, 0xC3, 0x87 };
static unsigned long antsTime = TickCount();
unsigned long t;
PenState ps;
t = TickCount();
if ( t >= ( antsTime + kAntsRate ))
{
antsTime = t;
GetPenState( &ps );
ShiftPattern( &antsPat );
PenPat( &antsPat );
PenSize( 1, 1 );
PenMode( patCopy );
FrameRgn( aRgn );
SetPenState( &ps );
}
}
/*----------------------------*** BALLOONHELPREFRESH ***------------------------------*/
/*
force balloon help to update by hiding any visible balloon. If the balloon is still needed,
it will be rebuilt and redisplayed, so you can call this if the item the balloon is
pointing to changes state.
----------------------------------------------------------------------------------------*/
void BalloonHelpRefresh()
{
if ( HMIsBalloon() && HMGetBalloons())
HMRemoveBalloon();
}